/// Memory Manage Unit
use crate::{
    address::*,
    memory::*,
    page_table::*,
};

pub fn vpn_to_ppn(page_table_ppn: PhysPageNum, vpn: VirtPageNum) -> Option<PhysPageNum> {
    let (index0, index1, index2) = vpn.parse();

    let mut page_table_ppn = page_table_ppn;
    for index in [index0, index1, index2].into_iter() {
        if let Some(ppn) = search_page_table(page_table_ppn, index) {
            page_table_ppn = ppn;
        } else {
            return None;
        }
    }

    Some(page_table_ppn)
}

pub fn vaddr_to_paddr(page_table_ppn: PhysPageNum, vaddr: VirtAddr) -> Option<PhysAddr> {
    let (vpn, offset) = vaddr.divide();
    if let Some(ppn) = vpn_to_ppn(page_table_ppn, vpn) {
        Some(PhysAddr::num_and_offset(ppn, offset))
    } else {
        None
    }
}

fn search_page_table(page_table_ppn: PhysPageNum, index: usize) -> Option<PhysPageNum> {
    let page_table_paddr = PhysAddr::num_and_offset(page_table_ppn, 0);
    let page_table = memory_get_ref::<PageTable>(page_table_paddr);
    let page_table_entry = page_table[index]; 
    if page_table_entry.is_valid() { 
        Some(page_table_entry.ppn()) 
    } else { 
        None 
    }
}

